/**
 * \file BinaryOp.h
 * \brief Headers and implementation of Expressions with two operands.
 * \author Wouter Caarls <w.caarls@tnw.tudelft.nl> 
 *
 * \verbatim
 * Copyright (c) 2005 Wouter Caarls, Delft University of Technology
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * \endverbatim
 */

#ifndef __BINARYOP_H_INCLUDED
#define __BINARYOP_H_INCLUDED

#include "Expression.h"

#define PREAMBLE(c)                                                  \
    c() : BinaryOp() {}                                              \
    c(Type *_type) : BinaryOp(_type) {}                              \
    c(Type *_type, Expression *_left, Expression *_right) :          \
      BinaryOp(_type, _left, _right) {}                              \
                                                                     \
    c *copy() { return new c(type, left->copy(), right->copy()); }   \
    c *construct(Type *_type, Expression *_left, Expression *_right) \
    {                                                                \
      return new c(_type, _left, _right);                            \
    }

class Lt : public BinaryOp
{
  public:
    PREAMBLE(Lt);
    int calculate(int a, int b) { return a < b; }
    double calculate(double a, double b) { return a < b; }
    std::string sign(void) { return (char*)CStr("<"); }
    std::string term(void) { return (char*)CStr("Lt"); }
};

class Gt : public BinaryOp
{
  public:
    PREAMBLE(Gt);
    int calculate(int a, int b) { return a > b; }
    double calculate(double a, double b) { return a > b; }
    std::string sign(void) { return (char*)CStr(">"); }
    std::string term(void) { return (char*)CStr("Gt"); }
};

class Le : public BinaryOp
{
  public:
    PREAMBLE(Le);
    int calculate(int a, int b) { return a <= b; }
    double calculate(double a, double b) { return a <= b; }
    std::string sign(void) { return (char*)CStr("<="); }
    std::string term(void) { return (char*)CStr("Le"); }
};

class Ge : public BinaryOp
{
  public:
    PREAMBLE(Ge);
    int calculate(int a, int b) { return a >= b; }
    double calculate(double a, double b) { return a >= b; }
    std::string sign(void) { return (char*)CStr(">="); }
    std::string term(void) { return (char*)CStr("Ge"); }
};

class Equal : public BinaryOp
{
  public:
    PREAMBLE(Equal);
    int calculate(int a, int b) { return a == b; }
    double calculate(double a, double b) { return a == b; }
    std::string sign(void) { return (char*)CStr("=="); }
    std::string term(void) { return (char*)CStr("Equal"); }
};

class NotEqual : public BinaryOp
{
  public:
    PREAMBLE(NotEqual);
    int calculate(int a, int b) { return a != b; }
    double calculate(double a, double b) { return a != b; }
    std::string sign(void) { return (char*)CStr("!="); }
    std::string term(void) { return (char*)CStr("NotEqual"); }
};

class Add : public BinaryOp
{
  public:
    PREAMBLE(Add);
    int calculate(int a, int b) { return a + b; }
    double calculate(double a, double b) { return a + b; }
    std::string sign(void) { return (char*)CStr("+"); }
    std::string term(void) { return (char*)CStr("Add"); }
};

class Subt : public BinaryOp
{
  public:
    PREAMBLE(Subt);
    int calculate(int a, int b) { return a - b; }
    double calculate(double a, double b) { return a - b; }
    std::string sign(void) { return (char*)CStr("-"); }
    std::string term(void) { return (char*)CStr("Subt"); }
};

class Mul : public BinaryOp
{
  public:
    PREAMBLE(Mul);
    int calculate(int a, int b) { return a * b; }
    double calculate(double a, double b) { return a * b; }
    std::string sign(void) { return (char*)CStr("*"); }
    std::string term(void) { return (char*)CStr("Mul"); }
};


class Div : public BinaryOp
{
  public:
    PREAMBLE(Div);
    int calculate(int a, int b) { return a / b; }
    double calculate(double a, double b) { return a / b; }
    std::string sign(void) { return (char*)CStr("/"); }
    std::string term(void) { return (char*)CStr("Div"); }
};

class Mod : public BinaryOp
{
  public:
    PREAMBLE(Mod);
    int calculate(int a, int b) { return a % b; }
    double calculate(double a, double b)
    {
      plog.warning() << HDR << "cannot take modulo of floating-point numbers" << std::endl;
      return (int) a % (int) b;
    }
    std::string sign(void) { return (char*)CStr("%"); }
    std::string term(void) { return (char*)CStr("Mod"); }
};

class LOr : public BinaryOp
{
  public:
    PREAMBLE(LOr);
    int calculate(int a, int b) { return a || b; }
    double calculate(double a, double b) { return a || b; }
    std::string sign(void) { return (char*)CStr("||"); }
    std::string term(void) { return (char*)CStr("LOr"); }
};

class LAnd : public BinaryOp
{
  public:
    PREAMBLE(LAnd);
    int calculate(int a, int b) { return a && b; }
    double calculate(double a, double b) { return a && b; }
    std::string sign(void) { return (char*)CStr("&&"); }
    std::string term(void) { return (char*)CStr("LAnd"); }
};

class And : public BinaryOp
{
  public:
    PREAMBLE(And);
    int calculate(int a, int b) { return a & b; }
    double calculate(double a, double b)
    {
      plog.warning() << HDR << "cannot take bitwise AND of floating-point numbers" << std::endl;
      return (int) a & (int) b;
    }
    std::string sign(void) { return (char*)CStr("&"); }
    std::string term(void) { return (char*)CStr("And"); }
};

class IncOr : public BinaryOp
{
  public:
    PREAMBLE(IncOr);
    int calculate(int a, int b) { return a | b; }
    double calculate(double a, double b)
    {
      plog.warning() << HDR << "cannot take bitwise inclusive OR of floating-point numbers" << std::endl;
      return (int) a | (int) b;
    }
    std::string sign(void) { return (char*)CStr("|"); }
    std::string term(void) { return (char*)CStr("IncOr"); }
};

class ExOr : public BinaryOp
{
  public:
    PREAMBLE(ExOr);
    int calculate(int a, int b) { return a ^ b; }
    double calculate(double a, double b)
    {
      plog.warning() << HDR << "cannot take bitwise exclusive OR of floating-point numbers" << std::endl;
      return (int) a ^ (int) b;
    }
    std::string sign(void) { return (char*)CStr("^"); }
    std::string term(void) { return (char*)CStr("ExOr"); }
};

class ShiftLeft : public BinaryOp
{
  public:
    PREAMBLE(ShiftLeft);
    int calculate(int a, int b) { return a << b; }
    double calculate(double a, double b)
    {
      plog.warning() << HDR << "cannot shift floating-point numbers" << std::endl;
      return (int) a << (int) b;
    }
    std::string sign(void) { return (char*)CStr("<<"); }
    std::string term(void) { return (char*)CStr("ShiftLeft"); }
};

class ShiftRight : public BinaryOp
{
  public:
    PREAMBLE(ShiftRight);
    int calculate(int a, int b) { return a >> b; }
    double calculate(double a, double b)
    {
      plog.warning() << HDR << "cannot shift floating-point numbers" << std::endl;
      return (int) a >> (int) b;
    }
    std::string sign(void) { return (char*)CStr(">>"); }
    std::string term(void) { return (char*)CStr("ShiftRight"); }
};

#endif /* __BINARYOP_H_INCLUDED */

